fix(state): resolve state bucket region in exports index store - #824
Merged
Conversation
PR #803 fixed LockManager to resolve a cross-region state bucket's actual region via GetBucketLocation before any S3 op. The automated cross-region-state-bucket integ then surfaced that the exports index store (Fn::ImportValue cross-stack reference tracking, writes _index/{region}/exports.json) had the same unfixed bug: its S3 client was pinned to the CLI base region, so against a bucket in another region every index write (after deploy) and remove (after destroy) hit S3's 301 PermanentRedirect, logged as "Exports index ... failed (non-retryable): ... must be addressed using the specified endpoint; continuing without index update". Non-fatal by design (the canonical state.json is written through the already-region-corrected S3StateBackend and the index self-heals), so the run passed while the cross-region index was silently never maintained. Port the LockManager.ensureClientForBucket() pattern into ExportIndexStore: before its first S3 read (readIndexRaw) or write (writeIndex) it resolves the bucket's region (cached process-wide via resolveBucketRegion, so it shares the state backend's / lock manager's existing GetBucketLocation call) and, if it differs from the supplied client's region, builds a private replacement S3Client for that region. The replacement reuses the caller's resolved credentials (so --profile / static creds carry over without threading client options through the four store call sites) and does NOT destroy the shared AwsClients.s3 instance other components still hold. The resolution is memoized + single-flight, and degrades gracefully for a test double whose client lacks the SDK config.region() shape. Contained inside the store, with no ripple to deploy.ts / destroy.ts / state.ts / local-state-loader.ts. Tests: 4 new unit tests (removeStack + updateForStack succeed through a region-corrected client when the bucket region differs; no rebuild when the resolved region matches; region resolved exactly once across multiple ops). Integ: the cross-region-state-bucket fixture stack now publishes a CloudFormation Output with an Export.Name (an export-less stack short-circuits the index write), and verify.sh greps the deploy + destroy output to assert the exports-index 301 warning is gone on both paths and that _index/{region}/exports.json was written to the cross-region bucket on deploy. New scenario tag exports-index-region-resolve. Closes #819
go-to-k
force-pushed
the
fix/819-exports-index-bucket-region
branch
from
June 13, 2026 06:21
9a6d8a7 to
c1cdac0
Compare
github-actions Bot
pushed a commit
that referenced
this pull request
Jun 13, 2026
## [0.220.4](v0.220.3...v0.220.4) (2026-06-13) ### Bug Fixes * **state:** resolve state bucket region in exports index store ([#824](#824)) ([663f6bd](663f6bd))
|
🎉 This PR is included in version 0.220.4 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The exports index store (
ExportIndexStore, used forFn::ImportValuecross-stack reference tracking) used an S3 client pinned to the CLI base region, so it hit301 PermanentRedirect("must be addressed using the specified endpoint; continuing without index update") when the state bucket lived in another region. This is the same class as #803 (just fixed forLockManager) — surfaced by the automatedcross-region-state-bucketinteg added in #803's PR.Fix (mirrors #803 / #60)
Ported the
ensureClientForBucket()pattern intosrc/state/export-index-store.ts(contained — no ripple to the 4 call sites): beforereadIndexRaw/writeIndex, resolve the bucket region via the process-wide-cachedresolveBucketRegion(shares the backend's/lock manager'sGetBucketLocation), and if it differs, build a private region-correctedS3Clientreusing caller credentials, without destroying the sharedAwsClients.s3. Memoized + single-flight; degrades gracefully for test doubles lackingconfig.region().Test plan
tests/unit/state/export-index-store.test.ts): 301→rebuild→success for index read + write (original client never used after rebuild); region resolved once (cached); existing 17 tests untouched.cross-region-state-bucket, extended with aCfnOutput+exportNameso the index write/remove path runs): deploy + destroy against a temporary us-west-2 bucket withAWS_REGION=us-east-1— PASS, 1 deleted, 0 errors, the log shows[ExportIndexStore] ... building a region-corrected S3 client for index operations, the_index/{region}/exports.jsonobject is written, and verify.sh asserts no 301 warning on deploy or destroy. Temp bucket cleaned.Independent review
Code review clean (1 nit: this is the 3rd copy of
ensureClientForBucket— extracting a sharedrebuildClientForBucketRegionhelper across S3StateBackend / LockManager / ExportIndexStore is a reasonable future cleanup, out of scope here).Closes #819